#include "BufferedJsonIoService.h"

static constexpr int MAX_MEDIA_BUFFER_SIZE = 20000;
static constexpr int BATCH_SIZE = 1000;

// public functions

BufferedJsonIoService::BufferedJsonIoService(int timer_interval)
    : _timer({0, timer_interval})
{
    _service.setReciever(this);
    _media_buffer.reserve(MAX_MEDIA_BUFFER_SIZE);
    // _max_buffer_size = max_buffer_size;
    _startTimer();
}

BufferedJsonIoService::~BufferedJsonIoService()
{
    _stopTimer();
}

void BufferedJsonIoService::setReciever(QObject * reciever)
{
    _reciever = reciever;
}

void BufferedJsonIoService::start(
    QString program
    , QStringList program_arguments
)
{
    _service.start(program, program_arguments);
}

bool BufferedJsonIoService::isRunning()
{
    return _service.isRunning();
}

void BufferedJsonIoService::stop()
{
    _media_buffer.clear();
    _service.stop();
}

void BufferedJsonIoService::write(QJsonDocument message)
{
    _service.write(message);
}

bool BufferedJsonIoService::event(QEvent * event)
{
    if (event->type() == JsonIoService::MessageEvent::TYPE)
    {
        return _messageEvent(
            static_cast<JsonIoService::MessageEvent *>(event)
        );
    }

    return QObject::event(event);
}

void BufferedJsonIoService::timerEvent(QTimerEvent * event)
{
    if (event->timerId() != _timer.id)
    {
        QObject::timerEvent(event);
        return;
    }

    if (_media_buffer.empty() || _reciever == nullptr)
    {
        return;
    }

    const int CURRENT_BATCH_SIZE = _media_buffer.size() < BATCH_SIZE
        ? _media_buffer.size()
        : BATCH_SIZE;
    QVector<QJsonDocument> messages_to_send;
    messages_to_send.reserve(CURRENT_BATCH_SIZE);
    for (int i = 0; i < CURRENT_BATCH_SIZE; ++i)
    {
        messages_to_send.append(_media_buffer.dequeue());
    }
    QCoreApplication::postEvent(
        _reciever
        , new JsonIoService::MessageEvent(messages_to_send)
        , -2
    );
}

bool BufferedJsonIoService::_messageEvent(
    JsonIoService::MessageEvent * event
)
{
    if (_reciever == nullptr) return true;

    QVector<QJsonDocument> normal_messages;
    for (const auto & message : qAsConst(event->messages))
    {
        auto message_object = !message.isObject()
            ? QJsonObject()
            : message.object();
        auto message_type = message_object.contains("type")
            ? message_object
                .constFind("type")
                .value()
                .toString()
            : "";
        if (message_type == "message" || message_type == "record")
        {
            if (MAX_MEDIA_BUFFER_SIZE < _media_buffer.size() + 1)
            {
                _media_buffer.dequeue();
            }
            _media_buffer.enqueue(message);
        }
        else
        {
            normal_messages.append(message);
        }
    }
    if (!normal_messages.empty())
    {
        QCoreApplication::postEvent(
            _reciever
            , new JsonIoService::MessageEvent(
                normal_messages
            )
            , Qt::NormalEventPriority
        );
    }
    return true;
}

// private functions

void BufferedJsonIoService::_startTimer()
{
    _stopTimer();

    _timer.id = startTimer(_timer.interval);
}

void BufferedJsonIoService::_stopTimer()
{
    if (_timer.id != 0)
    {
        killTimer(_timer.id);
        _timer.id = 0;
    }
}
